home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr47 / ertimer.zip / TIMER.C < prev    next >
Text File  |  1995-01-01  |  45KB  |  744 lines

  1. /***************************************************************************\
  2. **  TIMING CODE MODULE (80x86 specific code!)                              **
  3. **=========================================================================**
  4. **  Written by Ethan Rohrer, (c) Nuthing Software, December 4, 1994        **
  5. **                                                                         **
  6. **  Revision History                                                       **
  7. **  ----------------                                                       **
  8. **  Date        Description                                                **
  9. **  ---------   ---------------------------------------------------------  **
  10. **  13 May 94   Initial Release                                            **
  11. **  04 Dec 94   Updated code to conform to current coding standards/style  **
  12. **              Allowed appl. to specify # of timers as param. to TM_Init  **
  13. **              Standardized error handling with function _TM_Error        **
  14. **=========================================================================**
  15. **  This file contains code which makes use of system timer 0.  This timer **
  16. **  normally operates at a frequency of 1.1932MHz, regardless of the speed **
  17. **  of the CPU.  Normally, this timer is operating in mode 3 (square wave  **
  18. **  mode), and it completes a "cycle" in 0.054926 seconds (~1/18.207       **
  19. **  seconds).  The reason I say "normally" is because it is possible to    **
  20. **  change the length of the cycle, which changes the timer's frequency.   **
  21. **  This is NOT what the following code does.                              **
  22. **                                                                         **
  23. **  System timer 0 has its own 16 bit counter.  Here is some simplified    **
  24. **  pseudo-code of what system timer 0 does with its counter when it is    **
  25. **  operating in mode 3, and its frequency has not been tampered with:     **
  26. **                                                                         **
  27. **      counter = 65536                                                    **
  28. **      while (counter != 0)                                               **
  29. **          counter -= 2;                                                  **
  30. **      counter = 65536                                                    **
  31. **      while (counter != 0)                                               **
  32. **          counter -= 2;                                                  **
  33. **                                                                         **
  34. **  You read that right.  The counter is decremented from 65536 to 0       **
  35. **  TWICE.  This is somewhat unfortunate, because we want to read that     **
  36. **  counter to use for our timing operations because it is very accurate.  **
  37. **  The counter is decremented 65536 times in one timer cycle (32768       **
  38. **  times in each while loop above).  So, the time between decrements is:  **
  39. **                                                                         **
  40. **      (0.054926 seconds/cycle) / (65536 decrements/cycle) =              **
  41. **                      0.000000838 seconds/decrement = 838ns/decrement    **
  42. **                                                                         **
  43. **  Since the counter is decremented to 0 twice in each timer cycle, we    **
  44. **  would only be able to time events that take no longer than 0.027463    **
  45. **  seconds (one half of the timer cycle, the duration of one of the       **
  46. **  while loops above).                                                    **
  47. **                                                                         **
  48. **  The solution used by the code in this file is to change the timer's    **
  49. **  operation mode to mode 2.  Here is some simplified pseudo-code of what **
  50. **  system timer 0 does with its counter when it is operating in mode 2,   **
  51. **  and its frequency has not been tampered with:                          **
  52. **                                                                         **
  53. **      counter = 65536                                                    **
  54. **      while (counter != 0)                                               **
  55. **          counter -= 1;                                                  **
  56. **                                                                         **
  57. **  This solves any problems concerning the ambiguity of determining       **
  58. **  which while loop is executing when we read the counter from the timer. **
  59. **  But now, we have a 16 bit value which can only be used to time events  **
  60. **  which take no longer than 0.054926 seconds (duration of one cycle).    **
  61. **                                                                         **
  62. **  The solution used by this code is to make use of another timer:        **
  63. **  the "large timer", which is a 32-bit value at memory location          **
  64. **  0x40:0x6C.  Conveniently, this "large timer" is incremented each time  **
  65. **  system timer 0 completes a cycle (once each 0.054926 seconds).         **
  66. **  The code in this file generates 32-bit values to represent the         **
  67. **  time.  Obviously, we can't pack in the 16 bit counter and the 32 bit   **
  68. **  large timer into the 32 bit time type, so we cut off the high order    **
  69. **  16 bits of the large timer.  The following picture describes how the   **
  70. **  time value is generated using the timers:                              **
  71. **                                                                         **
  72. **    31                                        0 15                   0   **
  73. **    ------------------------------------------- ----------------------   **
  74. **    |         L a r g e   T i m e r           | |  65535 - Counter   |   **
  75. **    ------------------------------------------- ----------------------   **
  76. **                         |                     |                     |   **
  77. **                        \|/                   \|/                   \|/  **
  78. **                         V                     V                     V   **
  79. **                         31                  16 15                   0   **
  80. **                         ---------------------- ----------------------   **
  81. **                         |                 t T I M E                 |   **
  82. **                         ---------------------- ----------------------   **
  83. **                                                                         **
  84. **  (Note that we have to use (65535-Counter) because Counter is being     **
  85. **  decremented by the timer, but time is increasing)                      **
  86. **                                                                         **
  87. **=========================================================================**
  88. **  USING THIS MODULE                                                      **
  89. **      Before calling any other timing routine, you must call TM_Init(n), **
  90. **      where  n  specifies the number of timers your application needs.   **
  91. **      It may be a good idea to call TM_Init() in the initialization      **
  92. **      portion of your application.                                       **
  93. **                                                                         **
  94. **      To begin timing an event, make a call to TM_StartTimer(tid),       **
  95. **      where tid is an integer in the range [0..(n-1)] which specifies    **
  96. **      which of the n timers you are starting.                            **
  97. **                                                                         **
  98. **      To compute the duration of the event, just call                    **
  99. **      TM_ElapsedTime(tid), where tid is the same integer used in the     **
  100. **      call to TM_StartTimer().                                           **
  101. **                                                                         **
  102. **      When you are finished with the timing routines, call TM_Close().   **
  103. **      This should fit in nicely with the cleanup section of your         **
  104. **      application.                                                       **
  105. **                                                                         **
  106. **      If your application NEEDS to handle the time computations itself,  **
  107. **      the function TM_ReadTimer(tid) is also available.  This function   **
  108. **      will return the current time (MOD 1 hour, approximately).  I       **
  109. **      discourage use of this function, but discovered that I need it     **
  110. **      for another module/library...                                      **
  111. **                                                                         **
  112. **      EXAMPLES                                                           **
  113. **          A simple delaying routine:                                     **
  114. **              void delay( tTIME duration )                               **
  115. **              {                                                          **
  116. **                  TM_StartTimer(0);                                      **
  117. **                  while (TM_ElapsedTime(0) < duration)                   **
  118. **                      ;                                                  **
  119. **              }                                                          **
  120. **                                                                         **
  121. **          A fixed frame-rate game:                                       **
  122. **              TM_Init(1);                                                **
  123. **              while (player_not_dead)                                    **
  124. **              {                                                          **
  125. **                  TM_StartTimer(0);                                      **
  126. **                  MoveMonsters();                                        **
  127. **                  MovePlayers();                                         **
  128. **                  UpdateDisplay();                                       **
  129. **                  while (TM_ElapsedTime(0) < frame_duration)             **
  130. **                      ;                                                  **
  131. **              }                                                          **
  132. **              TM_Close();                                                **
  133. \***************************************************************************/
  134.  
  135. #include <dos.h>
  136. #include <stdio.h>
  137. #include <stdlib.h>
  138. #include "timer.h"
  139.  
  140. /*-------------------------------- MACROS ---------------------------------*/
  141.  
  142. /* macro to return the current value of the large timer */
  143.  
  144. #define  TM_LARGE_TIMER   (*((unsigned long far *)MK_FP(0x40, 0x6C)))
  145.  
  146. /*--------------------------- GLOBAL VARIABLES ----------------------------*/
  147.  
  148. /* starting times for all of the timers this code will support */
  149.  
  150. tTIME  *gpTM_start_time = NULL;
  151.  
  152. unsigned int gTM_max_timers = 0;
  153.  
  154. /* flag to let us know if it is ok to run the timing routines */
  155. /*     (1 = NOT safe, 0 = safe)                               */
  156.  
  157. unsigned char  gTM_module_not_initialized = 1;
  158.  
  159. /*------------------------- Error Message Strings -------------------------*/
  160.  
  161. #define TM_ERR_STR_GENERAL                                                 \
  162.     "General (unspecified) error."
  163. #define TM_ERR_STR_UNINITIALIZED                                           \
  164.     "Timing routines not yet initialized.\n"                           \
  165.     "(TM_Init() has not been called)"
  166. #define TM_ERR_STR_BAD_TIMER_ID                                            \
  167.     "Application specified an invalid timer ID."
  168. #define TM_ERR_STR_ALLOC                                                   \
  169.     "Unable to allocate dynamic memory."
  170. #define TM_ERR_STR_ZERO_TIMERS                                             \
  171.     "Application requested 0 timers.\n"                                \
  172.     "(must request 1 or more timers to use this module)"
  173.  
  174. /*------------------------- Error Message Indices -------------------------*/
  175. /* (Make sure these indices are accurate according to gpTM_error_text[]     */
  176. /* declared below !!)                                                      */
  177.  
  178. #define TM_ERR_GENERAL           0
  179. #define TM_ERR_UNINITIALIZED     1
  180. #define TM_ERR_BAD_TIMER_ID      2
  181. #define TM_ERR_ALLOC             3
  182. #define TM_ERR_ZERO_TIMERS       4
  183.  
  184. /*------------------------- Error Message Strings -------------------------*/
  185. /* (Make sure the positions of the error messages in this array are        */
  186. /* accurately represented by the error messages indices listed above !!)   */
  187.  
  188. char *gpTM_error_text[] =
  189. {
  190.     TM_ERR_STR_GENERAL,
  191.     TM_ERR_STR_UNINITIALIZED,
  192.     TM_ERR_STR_BAD_TIMER_ID,
  193.     TM_ERR_STR_ALLOC,
  194.     TM_ERR_STR_ZERO_TIMERS
  195. };
  196.  
  197. /***************************************************************************\
  198. **  void _TM_Error( )                                                      **
  199. *****************************************************************************
  200. **  ARGUMENTS                                                              **
  201. **      const char *pCalling_function_name                                 **
  202. **          (I) name of the calling function                               **
  203. **      int error_number                                                   **
  204. **          (I) integer identifier of the error that occurred              **
  205. **      const char *pCustom_message                                        **
  206. **          (I) additional message text to be displayed                    **
  207. **-------------------------------------------------------------------------**
  208. **  RETURNS                                                                **
  209. **      void                                                               **
  210. **-------------------------------------------------------------------------**
  211. **  EXAMPLE USAGE (NOT INTENDED FOR EXTERNAL USE)                          **
  212. **      if ( gTM_module_not_initialized )                                  **
  213. **      {                                                                  **
  214. **          _TM_Error ( pFunction_name, TM_ERR_UNINITIALIZED, NULL ); <-<< **
  215. **          return;                                                        **
  216. **      }                                                                  **
  217. **-------------------------------------------------------------------------**
  218. **  DETECTABLE ERROR CONDITIONS                                            **
  219. **      None                                                               **
  220. **-------------------------------------------------------------------------**
  221. **  DESCRIPTION                                                            **
  222. **      This function will generate a message which will be sent to stderr **
  223. **      to inform the user of an error.  This message will include the     **
  224. **      name of the function the error occurred in (if supplied), a canned **
  225. **      error string for the error indicated, and a custom string (if      **
  226. **      supplied) which may provide more details about th error.           **
  227. **-------------------------------------------------------------------------**
  228. **  LIMITATIONS                                                            **
  229. **      The message text must not exceed 1024 bytes in size, which can     **
  230. **      store over 12 80-character lines of text.                          **
  231. \***************************************************************************/
  232.  
  233. void _TM_Error ( pCalling_function_name, error_number, pCustom_message )
  234.     const char  *pCalling_function_name;
  235.     int          error_number;
  236.     const char  *pCustom_message;
  237. {
  238.     char error_message[1024];          /* buffer for message text */
  239.  
  240.     /*---------------------------------------------------------------------*\
  241.     **  Insert the "ERROR IN MODULE "TIMER"" header string into our        **
  242.     **  message.                                                           **
  243.     \*---------------------------------------------------------------------*/
  244.  
  245.     sprintf ( error_message, 
  246.           "\n******** ERROR IN MODULE \"TIMER\" *********\n" );
  247.  
  248.     /*---------------------------------------------------------------------*\
  249.     **  Insert the name of the function in which the error was discovered. **
  250.     **  This should always be provided, but check for NULL to be safe.     **
  251.     \*---------------------------------------------------------------------*/
  252.  
  253.     strcat ( error_message, "FUNCTION: " );
  254.  
  255.     if ( pCalling_function_name != (char *)NULL )
  256.     strcat ( error_message, pCalling_function_name );
  257.     else
  258.     strcat ( error_message, "<not specified - kill the programmer>" );
  259.  
  260.     strcat ( error_message, "\n" );
  261.  
  262.     /*---------------------------------------------------------------------*\
  263.     **  Insert the canned error message text for the specified error       **
  264.     **  number.                                                            **
  265.     \*---------------------------------------------------------------------*/
  266.  
  267.     strcat ( error_message, gpTM_error_text[error_number] );
  268.     strcat ( error_message, "\n" );
  269.  
  270.     /*---------------------------------------------------------------------*\
  271.     **  Insert the custom_message, if it is supplied.  This custom message **
  272.     **  should provide more detailed information than the generic error    **
  273.     **  strings.                                                           **
  274.     \*---------------------------------------------------------------------*/
  275.  
  276.     if ( pCustom_message != (char *)NULL )
  277.     {
  278.     strcat ( error_message, pCustom_message );
  279.     } /* end if ( custom message was supplied ) */
  280.  
  281.     strcat ( error_message, "\n" );
  282.  
  283.     /*---------------------------------------------------------------------*\
  284.     **  Send the message off to stderr.                                    **
  285.     \*---------------------------------------------------------------------*/
  286.  
  287.     fprintf ( stderr, "%s", error_message );
  288.  
  289. } /* end _TM_Error ( ) */
  290.  
  291. /***************************************************************************\
  292. **  tTIME TM_ReadTime( )                                                   **
  293. *****************************************************************************
  294. **  ARGUMENTS                                                              **
  295. **      void                                                               **
  296. **-------------------------------------------------------------------------**
  297. **  RETURNS                                                                **
  298. **      tTIME - the current time measured in units of 838ns                **
  299. **-------------------------------------------------------------------------**
  300. **  EXAMPLE USAGE (NOT INTENDED FOR EXTERNAL USE)                          **
  301. **      InitializeApplication();                                           **
  302. **      TM_Init(1);                                                        **
  303. **        . . .                                                            **
  304. **      current_time = TM_ReadTime( );        <-----<<                     **
  305. **        . . .                                                            **
  306. **      TM_Close();                                                        **
  307. **      ShutDownApplication();                                             **
  308. **-------------------------------------------------------------------------**
  309. **  DETECTABLE ERROR CONDITIONS                                            **
  310. **      *  Module has not been initialized (TM_Init() not called)          **
  311. **-------------------------------------------------------------------------**
  312. **  DESCRIPTION                                                            **
  313. **      This function generates a 32-bit value in units of 838ns.  This    **
  314. **      value spans a time period of about one hour.  The high 16 bits of  **
  315. **      this value come from the large timer (the 32-bit value at          **
  316. **      0x40:0x6C, the number of timer cycles since midnight), and the     **
  317. **      low 16 bits come from system timer 0's counter.                    **
  318. **                                                                         **
  319. **      NOTE: System timer 0's counter repeatedly cycles from 65535 down   **
  320. **            to 0 (decremented), but time is steadily increasing.  To     **
  321. **            get a steadily increasing value from this timer, we subtract **
  322. **            the actual value of the counter from 65535.                  **
  323. **-------------------------------------------------------------------------**
  324. **  LIMITATIONS                                                            **
  325. **      The value returned by this function is the current time MOD        **
  326. **      (approximately) one hour, not the time of day.  It is only useful  **
  327. **      useful for timing events that finish within an hour.               **
  328. \***************************************************************************/
  329.  
  330. tTIME TM_ReadTime ( void )
  331. {
  332.     register unsigned char LSB;        /* least significant byte           */
  333.     register unsigned char MSB;        /* most significant byte            */
  334.     char *pFunction_name =             /* This function's name, used for   */
  335.          "TM_ReadTime()";          /*     error reporting              */
  336.  
  337.     /*---------------------------------------------------------------------*\
  338.     **  Handle uninitialized module error.                                 **
  339.     \*---------------------------------------------------------------------*/
  340.  
  341.     if ( gTM_module_not_initialized )
  342.     {
  343.     _TM_Error ( pFunction_name, TM_ERR_UNINITIALIZED, NULL );
  344.     return( (tTIME) (-1) );
  345.     } /* end if */
  346.  
  347.     /*---------------------------------------------------------------------*\
  348.     **  By writing 0x00 to port 0x43, we are specifying the following      **
  349.     **  command:                                                           **
  350.     **      bits 76  = 00  --> timer 0                                     **
  351.     **      bits 54  = 00  --> reading 16-bit value: lsb followed by msb   **
  352.     **      bits 321 = 000 --> ignored                                     **
  353.     **      bit  0   = 0   --> ignored                                     **
  354.     \*---------------------------------------------------------------------*/
  355.  
  356.     outportb ( 0x43, 0x00 );
  357.  
  358.     /*---------------------------------------------------------------------*\
  359.     **  The following two statements reading from port 0x40 are reading    **
  360.     **  system timer 0's 16-bit counter: MSB after LSB.                    **
  361.     \*---------------------------------------------------------------------*/
  362.  
  363.     LSB = inportb ( 0x40 );                         /* get low order byte  */
  364.     MSB = inportb ( 0x40 );                         /* get high order byte */
  365.  
  366.     /*---------------------------------------------------------------------*\
  367.     **  Pack the time into the 32-bit tTIME return value.                  **
  368.     **  (see the preamble at the top of this file for more details)        **
  369.     \*---------------------------------------------------------------------*/
  370.  
  371.     return( (unsigned long int) (TM_LARGE_TIMER << 16)
  372.       | (unsigned int) (0xFFFF-((((unsigned int)MSB)<<8)|LSB)));
  373.  
  374. } /* end TM_ReadTime() */
  375.  
  376. /***************************************************************************\
  377. **  void TM_StartTimer( tid )                                              **
  378. *****************************************************************************
  379. **  ARGUMENTS                                                              **
  380. **      unsigned int tid                                                   **
  381. **          (I) integer label, which is used as an index into the array    **
  382. **              of timers (gpTM_start_time[])                              **
  383. **-------------------------------------------------------------------------**
  384. **  RETURNS                                                                **
  385. **      void                                                               **
  386. **-------------------------------------------------------------------------**
  387. **  EXAMPLE USAGE                                                          **
  388. **      #define THIRTYITH_OF_A_SECOND  39759                               **
  389. **      #define DELAY_TIMER            0                                   **
  390. **      InitializeApplication();                                           **
  391. **      TM_Init(1);                                                        **
  392. **        . . .                                                            **
  393. **      TM_StartTimer(DELAY_TIMER);           <-----<<                     **
  394. **      MoveMonsters();                                                    **
  395. **      MovePlayers();                                                     **
  396. **      UpdateDisplay();                                                   **
  397. **      while (TM_ElapsedTime(DELAY_TIMER) < THIRTYITH_OF_A_SECOND)        **
  398. **          ;                                                              **
  399. **        . . .                                                            **
  400. **      TM_Close();                                                        **
  401. **      ShutDownApplication();                                             **
  402. **-------------------------------------------------------------------------**
  403. **  DETECTABLE ERROR CONDITIONS                                            **
  404. **      *  Module has not been initialized (TM_Init() not called)          **
  405. **      *  tid is out of range (range = [0..(gTM_max_timers-1)])           **
  406. **-------------------------------------------------------------------------**
  407. **  DESCRIPTION                                                            **
  408. **      This procedure starts a pseudo-timer by reading the current time   **
  409. **      and storing it in the global array gpTM_start_time[].              **
  410. **-------------------------------------------------------------------------**
  411. **  LIMITATIONS                                                            **
  412. **      None                                                               **
  413. \***************************************************************************/
  414.  
  415. void TM_StartTimer ( tid )
  416.     unsigned int tid;
  417. {
  418.     char custom_message[256];          /* Buffer for custom error message  */
  419.     char *pFunction_name =             /* This function's name, used for   */
  420.          "TM_StartTimer()";        /*     error reporting              */
  421.  
  422.     /*---------------------------------------------------------------------*\
  423.     **  Handle uninitialized module error.                                 **
  424.     \*---------------------------------------------------------------------*/
  425.  
  426.     if (gTM_module_not_initialized)
  427.     {
  428.     _TM_Error ( pFunction_name, TM_ERR_UNINITIALIZED, NULL );
  429.     return;
  430.     } /* end if */
  431.  
  432.     if ( tid < gTM_max_timers ) /* then tid is valid */
  433.     {
  434.     /*-----------------------------------------------------------------*\
  435.     **  The timer ID is valid, so mark the starting time for this      **
  436.     **  timer (tid)                                                    **
  437.     \*-----------------------------------------------------------------*/
  438.  
  439.     gpTM_start_time[tid] = TM_ReadTime ( );
  440.     }
  441.     else /* tid is out of range */
  442.     {
  443.     /*-----------------------------------------------------------------*\
  444.     **  Handle the bad timer ID error.                                 **
  445.     \*-----------------------------------------------------------------*/
  446.  
  447.     sprintf ( custom_message, "Request received for timer %u, but the "
  448.                   "last valid timer is timer %u.\n",
  449.           tid, gTM_max_timers-1 );
  450.     _TM_Error ( pFunction_name, TM_ERR_BAD_TIMER_ID, custom_message );
  451.     } /* end if */
  452. } /* end TM_StartTimer() */
  453.  
  454. /***************************************************************************\
  455. **  tTIME TM_ElapsedTime( tid )                                            **
  456. *****************************************************************************
  457. **  ARGUMENTS                                                              **
  458. **      unsigned int tid                                                   **
  459. **          (I) integer label, which is used as an index into the array of **
  460. **              timers (gpTM_start_time[])                                 **
  461. **-------------------------------------------------------------------------**
  462. **  RETURNS                                                                **
  463. **      tTIME - the amount of time that has passed since the last call     **
  464. **              to TM_StartTimer(tid), measured in units of 838ns          **
  465. **-------------------------------------------------------------------------**
  466. **  EXAMPLE USAGE:                                                         **
  467. **      #define THIRTYITH_OF_A_SECOND  39759                               **
  468. **      #define DELAY_TIMER            0                                   **
  469. **      InitializeApplication();                                           **
  470. **      TM_Init(1);                                                        **
  471. **        . . .                                                            **
  472. **      TM_StartTimer(DELAY_TIMER);                                        **
  473. **      MoveMonsters();                                                    **
  474. **      MovePlayers();                                                     **
  475. **      UpdateDisplay();                                                   **
  476. **      while (TM_ElapsedTime(DELAY_TIMER) < THIRTYITH_OF_A_SECOND) <---<< **
  477. **          ;                                                              **
  478. **        . . .                                                            **
  479. **      TM_Close();                                                        **
  480. **      ShutDownApplication();                                             **
  481. **-------------------------------------------------------------------------**
  482. **  DETECTABLE ERROR CONDITIONS                                            **
  483. **      *  Module has not been initialized (TM_Init() not called)          **
  484. **      *  tid is out of range (range = [0..(gTM_max_timers-1)])           **
  485. **-------------------------------------------------------------------------**
  486. **  DESCRIPTION                                                            **
  487. **      This function calculates the time that has elapsed for timer slot  **
  488. **      tid since the last call to TM_StartTimer(tid) by getting the       **
  489. **      current time and subtracting the starting time (stored in global   **
  490. **      array gpTM_start_time[].)                                          **
  491. **-------------------------------------------------------------------------**
  492. **  LIMITATIONS                                                            **
  493. **      The largest elapsed time that can accurately be measured is        **
  494. **      approximately one hour:                                            ** 
  495. **          (tTIME = unsigned long int = 32-bits)                          **
  496. **          (65536(ticks/cycle)*18.2(cycles/sec)*3600 ~= 2^32)             **
  497. \***************************************************************************/
  498.  
  499. tTIME TM_ElapsedTime ( tid )
  500.     unsigned int tid;
  501. {
  502.     char custom_message[256];          /* Buffer for custom error message  */
  503.     char *pFunction_name =             /* This function's name, used for   */
  504.          "TM_ElapsedTime()";       /*     error reporting              */
  505.  
  506.     /*---------------------------------------------------------------------*\
  507.     **  Handle uninitialized module error.                                 **
  508.     \*---------------------------------------------------------------------*/
  509.  
  510.     if ( gTM_module_not_initialized )
  511.     {
  512.     _TM_Error( pFunction_name, TM_ERR_UNINITIALIZED, NULL );
  513.     return( (tTIME) (-1) );
  514.     } /* end if */
  515.     
  516.     if ( tid < gTM_max_timers ) /* then tid is valid */
  517.     {
  518.     /*-----------------------------------------------------------------*\
  519.     **  The timer ID is valid, so compute the elapsed time.            **
  520.     \*-----------------------------------------------------------------*/
  521.  
  522.     return( TM_ReadTime ( ) - gpTM_start_time[tid] );
  523.     }
  524.     else /* tid is out of range */
  525.     {
  526.     /*-----------------------------------------------------------------*\
  527.     **  Handle the bad timer ID error.                                 **
  528.     \*-----------------------------------------------------------------*/
  529.  
  530.     sprintf ( custom_message, "Request received for timer %u, but the "
  531.                   "last valid timer is timer %u.\n",
  532.           tid, gTM_max_timers-1 );
  533.     _TM_Error ( pFunction_name, TM_ERR_BAD_TIMER_ID, custom_message );
  534.     return( (tTIME) (-1) );
  535.     } /* end if */
  536. } /* end TM_ElapsedTime() */
  537.  
  538. /***************************************************************************\
  539. **  int TM_Init( )                                                         **
  540. *****************************************************************************
  541. **  ARGUMENTS                                                              **
  542. **      unsigned int num_timers                                            **
  543. **          (I) number of timers the application is requesting             **
  544. **-------------------------------------------------------------------------**
  545. **  RETURNS                                                                **
  546. **      int - non-zero means an error occurred                             **
  547. **            0 if the timer module was successfully initialized           **
  548. **-------------------------------------------------------------------------**
  549. **  EXAMPLE USAGE                                                          **
  550. **      InitializeApplication();                                           **
  551. **      TM_Init(1);                     <-----<<                           **
  552. **        . . .                                                            **
  553. **      ApplicationBody();                                                 **
  554. **        . . .                                                            **
  555. **      TM_Close();                                                        **
  556. **      ShutDownApplication();                                             **
  557. **-------------------------------------------------------------------------**
  558. **  DETECTABLE ERROR CONDITIONS                                            **
  559. **      * Invalid number of timers requested (0)                           **
  560. **      * Failed to allocated memory for array of pseudo-timers            **
  561. **-------------------------------------------------------------------------**
  562. **  DESCRIPTION                                                            **
  563. **      This procedure sets timer 0 to operation mode 2, and allocates     **
  564. **      memory to store starting times for the specified number of         **
  565. **      (pseudo)timers.                                                    **
  566. **                                                                         **
  567. **      The gTM_module_not_initialized is set to 0 to express that the     **
  568. **      module has been initialized, so the code can be used correctly.    **
  569. **-------------------------------------------------------------------------**
  570. **  LIMITATIONS                                                            **
  571. **      None                                                               **
  572. \***************************************************************************/
  573.  
  574. int TM_Init ( num_timers )
  575.     unsigned int num_timers;
  576. {
  577.     char custom_message[256];          /* Buffer for custom error message  */
  578.     char *pFunction_name =             /* This function's name, used for   */
  579.          "TM_Init()";              /*     error reporting              */
  580.  
  581.     /*---------------------------------------------------------------------*\
  582.     **  Make sure this code doesn't get executed more than once before     **
  583.     **  TM_Close().  We would allocate a new array of timers, and lose     **
  584.     **  the previously allocated array (memory leak).                      **
  585.     \*---------------------------------------------------------------------*/
  586.  
  587.     if ( !gTM_module_not_initialized )
  588.     {
  589.     return ( 0 );                /* return success                     */
  590.                      /* (this isn't an error, just stupid) */
  591.     }
  592.  
  593.     /*---------------------------------------------------------------------*\
  594.     **  Allocate an array of qseudo-timers                                 **
  595.     \*---------------------------------------------------------------------*/
  596.  
  597.     if ( num_timers > 0 )
  598.     {
  599.     gpTM_start_time = (tTIME *) calloc ( num_timers, sizeof ( tTIME ) );
  600.  
  601.     if ( gpTM_start_time == NULL )
  602.     {
  603.         sprintf ( custom_message,
  604.               "Failed to allocate %u timers of size %u.\n",
  605.               num_timers, (unsigned int) sizeof ( tTIME ) );
  606.         _TM_Error ( pFunction_name, TM_ERR_ALLOC, custom_message );
  607.         return ( -1 );
  608.     } /* end if */
  609.  
  610.     gTM_max_timers = num_timers;
  611.     }
  612.     else /* num_timers == 0, since num_timers is an unsigned int */
  613.     {
  614.     /*-----------------------------------------------------------------*\
  615.     **  Application requested 0 timers, which is pointless.            **
  616.     \*-----------------------------------------------------------------*/
  617.  
  618.     _TM_Error ( pFunction_name, TM_ERR_ZERO_TIMERS, NULL );
  619.     return ( -1 );
  620.     } /* end if-else */
  621.  
  622.     /*---------------------------------------------------------------------*\
  623.     **  Set timer to operation mode 2                                      **
  624.     **                                                                     **
  625.     **  By writing 0x34 to port 0x43, we are specifying:                   **
  626.     **     (0x34 = 00110100)                                               **
  627.     **     bits 76  = 00  --> timer 0                                      **
  628.     **     bits 54  = 11  --> writing 16-bit value: lsb followed by msb    **
  629.     **     bits 321 = 010 --> operation mode 2                             **
  630.     **     bit  0   = 0   --> binary counter operation                     **
  631.     \*---------------------------------------------------------------------*/
  632.  
  633.     outportb ( 0x43, 0x34 );
  634.  
  635.     /*---------------------------------------------------------------------*\
  636.     **  The following two statements writing 0x00 to port 0x40 are writing **
  637.     **  a 16-bit value of 0x0000 to the timer's counter, which specifies   **
  638.     **  that the counter is to begin its cycle with a value of 65536       **
  639.     **  (0x10000), which specifies a maximum length cycle (54.926ms).      **
  640.     \*---------------------------------------------------------------------*/
  641.  
  642.     outportb ( 0x40, 0x00 );
  643.     outportb ( 0x40, 0x00 );
  644.  
  645.     /*---------------------------------------------------------------------*\
  646.     **  Unlock this module                                                 **
  647.     \*---------------------------------------------------------------------*/
  648.  
  649.     gTM_module_not_initialized = 0;
  650.  
  651.     return ( 0 );                      /* initialization successful */
  652.  
  653. } /* TM_Init() */
  654.  
  655. /***************************************************************************\
  656. **  void TM_Close( )                                                       **
  657. *****************************************************************************
  658. **  ARGUMENTS                                                              **
  659. **      void                                                               **
  660. **-------------------------------------------------------------------------**
  661. **  RETURNS                                                                **
  662. **      void                                                               **
  663. **-------------------------------------------------------------------------**
  664. **  EXAMPLE USAGE                                                          **
  665. **      InitializeApplication();                                           **
  666. **      TM_Init(1);                                                        **
  667. **        . . .                                                            **
  668. **      ApplicationBody();                                                 **
  669. **       . . .                                                             **
  670. **      TM_Close();                     <-----<<                           **
  671. **      ShutDownApplication();                                             **
  672. **-------------------------------------------------------------------------**
  673. **  DETECTABLE ERROR CONDITIONS                                            **
  674. **      *  Module has not been initialized (TM_Init() not called)          **
  675. **-------------------------------------------------------------------------**
  676. **  DESCRIPTION                                                            **
  677. **     This procedure returns system timer 0 to operation mode 3, and      **
  678. **     deallocates the memory used to store the starting times for each    **
  679. **     of the (pseudo)timers.                                              **
  680. **                                                                         **
  681. **     The gTM_module_not_initialized is set to 1 to prevent further use   **
  682. **     of this module.  This module will remain locked until TM_Init()     **
  683. **     is called.                                                          **
  684. **-------------------------------------------------------------------------**
  685. **  LIMITATIONS                                                            **
  686. **      None                                                               **
  687. \***************************************************************************/
  688.  
  689. void TM_Close ( void )
  690. {
  691.     char *pFunction_name =             /* This function's name, used for   */
  692.          "TM_Close()";             /*     error reporting              */
  693.  
  694.     /*---------------------------------------------------------------------*\
  695.     **  Handle uninitialized module error.                                 **
  696.     \*---------------------------------------------------------------------*/
  697.  
  698.     if ( gTM_module_not_initialized )
  699.     {
  700.     _TM_Error ( pFunction_name, TM_ERR_UNINITIALIZED, NULL );
  701.     return;
  702.     } /* end if */
  703.  
  704.     /*---------------------------------------------------------------------*\
  705.     **  Deallocate the array of pseudo-timers                              **
  706.     \*---------------------------------------------------------------------*/
  707.  
  708.     if ( gpTM_start_time != NULL )
  709.     {
  710.     free ( gpTM_start_time );
  711.     gpTM_start_time = NULL;
  712.     } /* end if */
  713.  
  714.     /*---------------------------------------------------------------------*\
  715.     **  Set timer to operation mode 3                                      **
  716.     **                                                                     **
  717.     **  By writing 0x36 to port 0x43, we are specifying:                   **
  718.     **     (0x36 = 00110110)                                               **
  719.     **     bits 76  = 00  --> timer 0                                      **
  720.     **     bits 54  = 11  --> writing 16-bit value: lsb followed by msb    **
  721.     **     bits 321 = 011 --> operation mode 3                             **
  722.     **     bit  0   = 0   --> binary counter operation                     **
  723.     \*---------------------------------------------------------------------*/
  724.  
  725.     outportb ( 0x43, 0x36 );
  726.  
  727.     /*---------------------------------------------------------------------*\
  728.     **  The following two statements writing 0x00 to port 0x40 are writing **
  729.     **  a 16-bit value of 0x0000 to the timer's counter, which specifies   **
  730.     **  that the counter is to begin its cycle with a value of 65536       **
  731.     **  (0x10000), which specifies a maximum length cycle (54.926ms).      **
  732.     \*---------------------------------------------------------------------*/
  733.  
  734.     outportb ( 0x40, 0x00 );
  735.     outportb ( 0x40, 0x00 );
  736.  
  737.     /*---------------------------------------------------------------------*\
  738.     **  Lock this module                                                   **
  739.     \*---------------------------------------------------------------------*/
  740.  
  741.     gTM_module_not_initialized = 1;
  742.  
  743. } /* end TM_Close ( ) */
  744.